package language.monkey.laxer

import language.monkey.token.Token
import language.monkey.token.TokenType

class Lexer(private var input: String) {
    /**
     * 当前读取的字符位置
     */
    private var position: Int = 0

    /**
     * 下一个要读取的字符位置
     */
    private var readPosition: Int = 0

    /**
     * 当前读取的字符
     */
    private var ch: Char = 0.toChar()

    init {
        readChar()
    }

    /**
     * 读取下一个字符
     */
    fun readChar() {
        ch = if (readPosition >= input.length) {
            0.toChar()
        } else {
            input[readPosition]
        }
        position = readPosition
        readPosition++
    }

    /**
     * 读取下一个字符，不移动字符位置
     */
    fun peekChar(): Char {
        if (readPosition >= input.length) {
            0.toChar()
        }
        return input[readPosition]
    }


    fun nextToken(): Token {
        var tok: Token

        // 跳过空白字符
        skipWhiteSpace()

        when (ch) {
            // 优先处理关键字
            '=' -> {
                if (peekChar() == '=') {
                    readChar()
                    tok = Token.newToken(TokenType.EQ, "==")
                } else {
                    tok = Token.newToken(TokenType.ASSIGN, ch)
                }
            }

            ';' -> tok = Token.newToken(TokenType.SEMICOLON, ch)
            '(' -> tok = Token.newToken(TokenType.LPAREN, ch)
            ')' -> tok = Token.newToken(TokenType.RPAREN, ch)
            ',' -> tok = Token.newToken(TokenType.COMMA, ch)
            '+' -> tok = Token.newToken(TokenType.PLUS, ch)
            '{' -> tok = Token.newToken(TokenType.LBRACE, ch)
            '}' -> tok = Token.newToken(TokenType.RBRACE, ch)
            '-' -> tok = Token.newToken(TokenType.MINUS, ch)
            '*' -> tok = Token.newToken(TokenType.ASTERISK, ch)
            '/' -> tok = Token.newToken(TokenType.SLASH, ch)
            '<' -> tok = Token.newToken(TokenType.LT, ch)
            '>' -> tok = Token.newToken(TokenType.GT, ch)
            '!' -> {
                if (peekChar() == '=') {
                    readChar()
                    tok = Token.newToken(TokenType.NOT_EQ, "!=")
                } else {
                    tok = Token.newToken(TokenType.BANG, ch)
                }
            }

            0.toChar() -> tok = Token.newToken(TokenType.EOF, 0.toChar())
            else -> {
                if (isLetter(ch)) {
                    return Token.newByLiteral(readIdentifier())
                } else if (isDigit(ch)) {
                    return Token.newToken(TokenType.INT, readNumber())
                } else {
                    tok = Token.newToken(TokenType.ILLEGAL, ch)
                }
            }
        }
        readChar()
        return tok
    }

    private fun skipWhiteSpace() {
        while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') {
            readChar()
        }
    }

    fun readIdentifier(): String {
        val position = this.position

        while (isLetter(ch)) {
            readChar()
        }
        return input.substring(position, this.position)
    }

    fun readNumber(): String {
        val position = this.position
        while (isDigit(ch)) {
            readChar()
        }
        return input.substring(position, this.position)
    }


    companion object {
        /**
         * 判断当前字符是否是字母
         */
        fun isLetter(ch: Char): Boolean {
            return ch in 'a'..'z' || ch in 'A'..'Z' || ch == '_'
        }

        fun isDigit(ch: Char): Boolean {
            return ch in '0'..'9'
        }


    }


}